package io.gitee.caoxiaoyu97.log;

import org.slf4j.Logger;
import org.slf4j.MDC;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/*********************************************************
 * 文件名称：Logger.java
 * 系统名称：CXY自定义系统
 * 模块名称：io.gitee.caoxiaoyu97.log
 * 功能说明：
 * 开发人员 @author：caoxy31978
 * 开发时间 @date：2023/7/7 15:00
 * 修改记录：程序版本  修改日期  修改人员  修改单号  修改说明
 *********************************************************/
public class LoggerProxy {

    /**
     * 代理Logger对象，增强日志记录功能。
     *
     * @param logger 要代理的Logger对象。
     * @return 代理后的Logger对象。
     */
    public static Logger getLoggerProxy(Logger logger) {
        ClassLoader classLoader = Logger.class.getClassLoader();
        Class<?>[] interfaces = new Class[] { Logger.class, io.gitee.caoxiaoyu97.log.Logger.class};
        InvocationHandler invocationHandler = new LoggerInvocationHandler(logger);
        return (Logger) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
    }

    /**
     * 日志记录增强处理器。
     */
    private static class LoggerInvocationHandler implements InvocationHandler {

        private static volatile boolean isAsync = true;

        private static volatile boolean asyncIsInit = false;

        private static final LinkedBlockingQueue<Message> concurrentLinkedQueue = new LinkedBlockingQueue<>();

        private static void initAsyncLogConsumer() {
            if (!asyncIsInit){
                synchronized (concurrentLinkedQueue){
                    if (!asyncIsInit){
                        new Thread(()->{
                            while (true){
                                try {
                                    Message poll = concurrentLinkedQueue.take();
                                    doLog(poll);
                                } catch (InterruptedException e) {
                                    break;
                                }
                            }
                        }).start();
                        asyncIsInit = true;
                    }
                }
            }
        }

        /**
         * 默认最大buffer深度
         */
        private static final int DEFAULT_MAX_BUFFER_RECORDS = 80000;

        /**
         * 最大buffer深度
         */
        private int maxBufferRecords = DEFAULT_MAX_BUFFER_RECORDS;
        /**
         * 当前buffer深度
         */
        private int bufferRecords = 0;

        private boolean supportTransaction = true;
        private ThreadLocal<LogBuffer> threadLocal = new ThreadLocal();
        private final Logger logger;

        public LoggerInvocationHandler(Logger logger) {
            this.logger = logger;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if ("startTransaction".equals(method.getName())){
                startTransaction();
                return null;
            }
            if ("endTransaction".equals(method.getName())){
                endTransaction();
                return null;
            }
            Set<String> printingMethodNames = new HashSet<>();
            printingMethodNames.add("trace");
            printingMethodNames.add("debug");
            printingMethodNames.add("info");
            printingMethodNames.add("warn");
            printingMethodNames.add("error");
            if (!printingMethodNames.contains(method.getName())){
                return method.invoke(logger,args);
            }else{
                exportLog(method,args);
            }
            return null;
        }
        private void exportLog(Method method, Object[] args) {
            LogBuffer logBuffer = getLogBuffer();
            Message logMsg = new Message(logger, method, args,Thread.currentThread());
            if (logBuffer!= null && logBuffer.getTimes() > 0){
                logBuffer.addMessage(logMsg);
                checkBufferSize(logBuffer);
            }else{
                doLog(logMsg);
            }
        }

        private void checkBufferSize(LogBuffer logBuffer) {
            ++bufferRecords;
            if (bufferRecords >= maxBufferRecords) {
                flushLog(logBuffer);
            }
        }

        public void flushLog(LogBuffer logBuffer) {
            if (isAsync){
                synchronized(concurrentLinkedQueue) {
                    initAsyncLogConsumer();
                    for (Message logMessage : logBuffer.getLogMessages()) {
                        try {
                            concurrentLinkedQueue.put(logMessage);
                        } catch (InterruptedException e) {
                        }
                    }
                }
            }else{
                List<List<Message>> lists = splitList(logBuffer.getLogMessages(), 10);
                for (List<Message> list : lists) {
                    try {
                        Thread.sleep(0);
                    } catch (InterruptedException e) {
                    }
                    synchronized (LoggerInvocationHandler.class){
                        for (Message logMessage : list) {
                            doLog(logMessage);
                        }
                    }
                }
            }

            logBuffer.getLogMessages().clear();
            bufferRecords = 0;
        }
        /**
         * 将一个List按照指定大小分为多个List。
         *
         * @param sourceList 原始List。
         * @param size 指定的大小。
         * @return 分割后的List的列表。
         */
        public static <T> List<List<T>> splitList(List<T> sourceList, int size) {
            if (sourceList == null || sourceList.isEmpty() || size <= 0) {
                return new ArrayList<>();
            }

            int totalSize = sourceList.size();
            int count = (totalSize + size - 1) / size;

            return IntStream.range(0, count)
                    .mapToObj(i -> sourceList.subList(i * size, Math.min((i + 1) * size, totalSize)))
                    .collect(Collectors.toList());
        }

        private static void doLog(Message logMessage) {
            try {
                String oldThreadName = Thread.currentThread().getName();
                Thread.currentThread().setName(logMessage.getThreadName());
                logMessage.getMethod().invoke(logMessage.getLogger(), logMessage.getArgs());
                Thread.currentThread().setName(oldThreadName);
            } catch (Exception e) {
            }
        }

        private synchronized LogBuffer getLogBuffer() {
            if (supportTransaction){
                LogBuffer logBuffer = threadLocal.get();
                if (logBuffer == null) {
                    logBuffer = new LogBuffer();
                    threadLocal.set(logBuffer);
                }
                return logBuffer;
            }
            return null;
        }
        public void removeLogBuffer() {
            threadLocal.set(null);
        }
        public void startTransaction() {
            LogBuffer logBuffer = this.getLogBuffer();
            if (logBuffer != null) {
                logBuffer.increaseTransactionDepth();
            }
        }
        public void endTransaction() {
            LogBuffer logBuffer = getLogBuffer();
            if (logBuffer != null) {
                logBuffer.decreaseTransactionDepth();
                if (logBuffer.getTimes() == 0) {
                    flushLog(logBuffer);
                    removeLogBuffer();
                }
            }
        }
    }


}